/*
 * Creta is an editor WYSIWYG with mind in speed and lightweight written in pure JS.
 *
 * Main class for creta editor
 * It is a composition of two classes core and ui.
 * 
 * This code is under AGPLV3 by alkeon (alkeon@autistici.org).
 *
 * TODO:
 * 	Multiple line selection
 *	Avoid global vars
 * 	Simplify keydown code
 */

/*jslint browser: true*/

"use strict";

/*jshint esversion: 6 */

//Global var for selection
var range_link;
var range_block;
var selected_for_visual = "";
var selected_for_visual_line = "";

class creta {

	/*
	 * Constructor. Set options in creta editor, set editor shortcuts and keydown events.
	 *
	 */
	constructor(id, element, options) {
		this.id = id;
		this.element = element;
		this.b64_images = [];
		if(typeof options !== 'undefined') {
			
			this.has_clear_button  = options.clear_button;
			this.has_link_button = options.link;
			this.has_heading_button = options.heading;
			this.has_list_button = options.list;
			this.has_table_button = options.table;
			this.has_image_button = options.image;
			this.has_block_button = options.block;
			this.has_code_button = options.code;
			this.has_embed_button = options.embed;
			this.has_save_button = options.save;
			this.has_load_button = options.load;
		}
		let ui = new creta_ui(element, options);
		let core = new creta_core(id, element);

		this.ui = ui;
		this.core = core;
		
		element.addEventListener('keydown', function (e){
			
			ui.clean_keybinding(e);
			ui.link_keybinding(e);
			ui.image_keybinding(e);
			ui.table_keybinding(e);
			core.list_keybinding(e);
			ui.block_keybinding(e);
			core.code_keybinding(e);
			ui.embed_keybinding(e);
			core.heading_keybinding(e);
			
		});
		
		element.getElementsByClassName('visual-editor')[0].addEventListener('input', function (e){
			core.editor_actions(e);
		});
		
		element.getElementsByClassName('visual-editor')[0].addEventListener('keydown', function (e){
			core.editor_actions(e);
		});
		
		var all_popups = this.element.getElementsByClassName('popup');
		for(let popup_position = 0; popup_position < all_popups.length; ++popup_position){
			all_popups[popup_position].addEventListener("keydown", function(event) {
				if(event.key == 'Enter') {
					event.target.parentElement.getElementsByTagName('button')[0].click();
					event.preventDefault();
				}else if((event.key == 'q') && event.altKey){
					event.target.parentElement.style.display = "none";
					return false;
				}

			});
		}
		this.set_listeners();
		element.className += ' content-wrapper column white-background';
	}
	
	/*
	 * Add external UI element in toolbox
	 *
	 */
	add_ui_element(editor, element){
		let toolbox = this.element.getElementsByClassName('edit-controls');
		toolbox[0].append(element);
	}
	
	/*
	 * Hide/Unhide element
	 *
	 */
	change_visibility(element){
		this.ui.change_visibility(element);
	}
	
	/*
	 * Set toolbars, popup listeners and paste listener
	 *
	 */
	set_listeners(){
		let core = this.core;
		if(this.has_clear_button)
			this.set_listener('clean', function(e){ core.clean(e); });
		if(this.has_heading_button)
			this.set_listener('heading_toolbar', function (e){
				let command = e.currentTarget.sourceTarget.getElementsByClassName('heading_toolbar')[0].value;
				core.change_line_format(command);
				e.currentTarget.sourceTarget.getElementsByClassName('heading_toolbar')[0].selectedIndex = 0;
			});
		if(this.has_list_button)
			this.set_listener('list_toolbar', function (e){
				core.create_list();
			});
		if(this.has_link_button){
			this.set_listener('link_toolbar', function(e){ core.link_toolbar(e); });
			
			this.set_listener('accept_link', function (e){
				var link = e.currentTarget.sourceTarget.getElementsByClassName('link_input')[0].value;
				if(link != ""){
					if(!link.includes("://")){
						link = "https://" + link;
					}
					core.create_link(link);
					e.currentTarget.sourceTarget.getElementsByClassName('link_popup')[0].style.display = "none";
				}
			});
		}
		if(this.has_table_button){
			this.set_listener('table_toolbar', function(e){ core.table_toolbar(e); });
			
			this.set_listener('accept_table', function (e){
				var w = e.currentTarget.sourceTarget.getElementsByClassName('w')[0].valueAsNumber;
				var h = e.currentTarget.sourceTarget.getElementsByClassName('h')[0].valueAsNumber;
				
					
				core.create_table(w, h);
				e.currentTarget.sourceTarget.getElementsByClassName('table_popup')[0].style.display = "none";
			});
			
		}
		if(this.has_image_button){
			this.set_listener('image_toolbar', function(e){ core.image_toolbar(e); }, "change");
			this.set_listener('accept_image', function (e){
				var alt = e.currentTarget.sourceTarget.getElementsByClassName('alt_input')[0].value;
				
					
				core.create_image(alt);
				e.currentTarget.sourceTarget.getElementsByClassName('image_popup')[0].style.display = "none";
			});
		}
		if(this.has_block_button){
			this.set_listener('block_toolbar', this.ui.block_toolbar);
			this.set_listener('accept_block', function (e){
				let block_class = e.currentTarget.sourceTarget.getElementsByClassName('block_input')[0].value;
				
				core.create_block(block_class);
				e.currentTarget.sourceTarget.getElementsByClassName('block_popup')[0].style.display = "none";
			});
		}
		if(this.has_code_button)
			this.set_listener('code_toolbar', function (e){core.create_code();});
		if(this.has_embed_button)
			this.set_listener('embed_toolbar', function(e){ core.embed_toolbar(e); });
		if(this.has_save_button)
			this.set_listener('save_toolbar', function(){core.save(); });
		if(this.has_load_button)
			this.set_listener('load_toolbar', function(){core.load(); });
			
		this.element.getElementsByClassName('visual-editor')[0].addEventListener('paste', function (e){
			core.paste(e);
		});

	}
	
	/*
	 * Wrap method for addEventListener and send source element as sourceTarget.
	 *
	 */
	set_listener(className, function_name, event = "click"){
		this.element.getElementsByClassName(className)[0].sourceTarget = this.element;
		this.element.getElementsByClassName(className)[0].addEventListener(event, function_name);
	}

	load(){ this.core.load(); }
	save(){ this.core.save(); }
	
	/*
	 * Get HTML content from editor
	 * - Images are saved in b64_images array
	 * - returned text has images as the form imagesN where N is position in array
	 *
	 */
	get_text() {
	
		let images = this.element.querySelector('.visual-editor').getElementsByTagName("img");
		for(let i = 0; i < images.length; ++i){
			this.b64_images.push(images[i].src);
			images[i].src = ("image" + (this.b64_images.length - 1));
		}
		var s = this.element.querySelector('.visual-editor').innerHTML;

		s = s.replace(/><br></g, "><");
		s = s.replace(/ contenteditable=\"true\"/g, "");
		s = this.prepare_for_conversion(s);
		s = s.replace(/<br>\n/g, "\n");
		s = s.replace(/<br><\/li>\n/g, "\n");
		s = s.replace(/<\/div>\n[ ]*<\/div>/g, "</div></div>");
		s = s.replace(/<div>\n[ ]*<div/g, "<div><div");
		
		images = this.element.querySelector('.visual-editor').getElementsByTagName("img");
		for(let i = 0; i < images.length; ++i){
			images[i].src = (this.b64_images[i]);
		}
		return s;
	}
	
	/*
	 * Set HTML content in editor and listeners
	 *
	 */
	set_text(s){
	
		if(s[s.length - 1] == '\n')
			s = s.substring(0, s.length - 1);

		s = this.prepare_for_editor(s);
		s = s.replace(/<table/g, '<div class="deletebutton editor-div"><button type="checkbox" class="delete_table"><\/button><\/div><table');
		s = s.replace(/> </g, "> <");

		this.element.querySelector('.visual-editor').innerHTML = s;
		
		let images = this.element.querySelector('.visual-editor').getElementsByTagName("img");
		for(let i = 0; i < this.b64_images.length && i < images.length; ++i){
			var parent = images[i].parentNode;
			var wrapper = document.createElement('div');
			wrapper.className = "image editor-div";
			images[i].src = (this.b64_images[i]);

			let image = images[i];
			parent.replaceChild(wrapper, images[i]);
			
			wrapper.appendChild(image);
			
		}
		
		this.b64_images = [];
		this.core.set_delete_table_listeners();
		this.core.set_delete_image_listeners();
	}
	
	/*
	 * Get text content from editor
	 *
	 */
	get_text_code() {
		var s = this.element.querySelector('.visual-editor').innerHTML;

		s = s.replace(/&nbsp;/g, " ");
		
		s = s.replace(/ contenteditable=\"true\"/g, "");
		s = html_beautify(s);
		s = s.replace(/>[ \t]*--/g, ">--");
		s = this.prepare_for_conversion(s);
		s = s.replace(/^<div>/g, "");
		s = s.replace(/<\/div>\n<div>/g, "\n");
		s = s.replace(/<\/div>\n/g, "\n");
		s = s.replace(/<\/div>$/, "");

		s = s.replace(/^&lt;&gt;/g, "<>");
		s = s.replace(/^&lt;/g, "<");
		s = s.replace(/\n&lt;/g, "\n<");
		s = s.replace(/<&gt;/g, "<>");
		return s;
	}
	
	/*
	 * Set text content in editor and listeners
	 *
	 */
	set_text_code(s){
	
		if(s[s.length - 1] == '\n')
			s = s.substring(0, s.length - 1);
			
		s = this.prepare_for_editor(s);

		this.element.querySelector('.visual-editor').innerHTML = s;

		this.core.set_delete_table_listeners();
		this.core.set_delete_image_listeners();
		
	}
	
	get_images_b64(){
		return this.b64_images;
	}
	
	set_images_b64(images_b64){
		this.b64_images = images_b64;
	}
	
	/*
	 * Add editor special div to these lines
	 * - lines with headings (h1/h2/h3/h4/h5 ...)
	 * - lines with lists (whole list in a editor line)
	 * - code lines
	 * - text only lines
	 *
	 */
	prepare_for_editor(s) {
		var index = s.split('\n');
		var return_text = "";
		var unordered_list = 0;
		var i = 0;
		let start_list = 0;
		while(i < index.length){
			//If there's an ul start tag in end of other line, put ul tag in new line
			let ul_start = index[i].search("<ul");
			if(ul_start != -1 && !this.is_first_element(index[i], ul_start) && !unordered_list){
				index.splice(i, 1, index[i].substring(0, ul_start), "<ul>");
			}
			if(index[i].search("<h") != -1)
				return_text += "<div contenteditable=\"true\">" + index[i] + "</div>\n";
			else if(this.is_first_element(index[i], index[i].search("<ul")) && !unordered_list){
				return_text += "<div contenteditable=\"true\">" + index[i];
				unordered_list++;
			}else if((index[i].search("</ul>") != -1) && unordered_list == 1){
				return_text += index[i] + "</div>";
				unordered_list--;
			}else if(index[i].search("<ul") != -1){
				return_text += index[i];
				++unordered_list;
			}else if(index[i].search("</ul>") != -1){
				return_text += index[i];
				--unordered_list;
			}else if(index[i].search("<li>") != -1)
				return_text += index[i];
			else if(index[i].search("<code>") != -1)
				return_text += "<div contenteditable=\"true\">" + index[i] + "</div>\n";
			else
				return_text += "<div contenteditable=\"true\">" + index[i] + "</div>\n";
			++i;
		}
		return return_text;
	}
	
	/*
	 * Check if position is first element in line (only allows spaces and tabulates)
	 *
	 */
	is_first_element(line, position){
		let i = 0;
		while(i < position && (line[i] == ' ' || line[i] == '\t'))
			++i;
		return (i >= 0 && i == position);
	}
	
	/*
	 * Delete editor tags, reformat and re-indent with html_beautify
	 *
	 */
	prepare_for_conversion(s){
		if(s == "<div></div>"){ s = ""; }
		s = s.replace(/<div><h1>/g, "<h1>");
		s = s.replace(/<div><h2>/g, "<h2>");
		s = s.replace(/<div><h3>/g, "<h3>");
		s = s.replace(/<\/h1><\/div>/g, "</h1>");
		s = s.replace(/<\/h2><\/div>/g, "</h2>");
		s = s.replace(/<\/h3><\/div>/g, "</h3>");
		s = s.replace(/<h.><\/h.>/g, "");
		s = s.replace(/<div><div class="image editor-div">/g, "");
		s = s.replace(/<div class="image editor-div">/g, "\n");
		s = s.replace(/<div class="table">/g, "");
		s = s.replace(/<div><div class="deletebutton editor-div"><button type="checkbox" class="delete_table"><\/button><\/div>/g, "");
		s = s.replace(/<div>\n	<div class="deletebutton editor-div"><button type="checkbox" class="delete_table"><\/button><\/div>\n/g, "");
		s = s.replace(/<div class="deletebutton editor-div"><button type="checkbox" class="delete_table"><\/button><\/div>/g, "");

		s = html_beautify(s);
		
		s = s.replace(/^<code>/g, "<div><code>");
		s = s.replace(/<\/code>\n/g, "</code></div>\n");

		return s;
	}
	
	is_scrolled_into_view(elem){ return this.core.is_scrolled_into_view(elem); }

}

